#!/usr/bin/env bash
# Copyright (c) 2021 maminjie <maminjie2@huawei.com>
# SPDX-License-Identifier: MulanPSL-2.0

method_def list branch checkout update importsrcpkg setflag buildinfo
method_def getbin getbinaries getbinlist getfile unresolved
method_def pkgresults getbuildlogs getjobtime rdelete rebuild


usage_branch() {
    echo "branch (br): Branch some packages from obs"
    echo ""
    echo "usage:"
    echo "    ${PROG} br SOURCEPROJECT TARGETPROJECT PACKAGES|LISTFILE"
    echo ""
}

alias_def branch br
# do_branch srcprj dstprj pkgs
do_branch() {
    if [ $# -ne 3 ]; then
        usage_branch; exit
    fi
    local pkgs=$(get_list "$3")

    local k=1
    for pkg in $pkgs; do
        echo "$k) branching $1/$pkg"
        osc branch "$1" "$pkg" "$2" 2> /dev/null
        ((k++))
    done
}


# obs_check_project [prj]
#   Check if the directory is an obs project
obs_check_project() {
    local prj=$1
    if [ -z "$prj" ]; then
        prj=$(pwd)
    elif [ ! -d "$prj" ]; then
        return 1
    fi
    ls -a "$prj"/.osc/_project &>/dev/null
    if [ $? -ne 0 ]; then
        return 1
    fi
    return 0
}

usage_checkout() {
    echo "checkout (co): Check out content from the repository of obs"
    echo ""
    echo "usage:"
    echo "    ${PROG} co SOURCEPROJECT PACKAGES|LISTFILE"
    echo "    ${PROG} co PACKAGES|LISTFILE                  # current dir must be obs project"
    echo "    ${PROG} co SOURCEPROJECT/PACKAGE"
    echo ""
}

alias_def checkout co
# do_checkout srcprj pkgs
# do_checkout pkgs
# do_checkout srcprj/pkg
do_checkout() {
    local prj=""
    local pkgs=""
    if [ $# -eq 2 ]; then
        prj=$1
        pkgs=$(get_list "$2")
    elif [ $# -eq 1 ]; then
        # prj/pkg
        eval $(echo "$1" | awk -F "/" '{
            if (NF >= 2) {
                printf("prj=%s;pkgs=%s;", $(NF-1), $NF)
            }
        }')
        if [ -z "$prj" ]; then
            obs_check_project
            if [ $? -ne 0 ]; then
                usage_checkout; exit
            fi
            pkgs=$(get_list "$1")
        fi
    else
        usage_checkout; exit
    fi

    local k=1
    for pkg in $pkgs; do
        local pkg_path=$prj/$pkg
        if [ -z "$prj" ]; then
            pkg_path=$pkg
        fi
        echo "$k) checking out $pkg_path"
        osc co "$pkg_path" 2> /dev/null
        pushd "$pkg_path" || return 1
        osc_update
        popd || return 1
        ((k++))
    done
}


usage_importsrcpkg() {
    echo "importsrcpkg (isp): Import some new packages from src.rpm"
    echo ""
    echo "usage:"
    echo "     ${PROG} importsrcpkg PROJECT PACKAGES|LISTFILE ..."
    echo ""
    echo "params:"
    echo "    PROJECT - obs project"
    echo "    ... - yumdownloader global options, like: --repo reponame"
    echo ""
    echo "notes:"
    echo "    The obs PROJECT directory must exist in the current directory"
    echo ""
}

alias_def importsrcpkg isp
# do_importsrcpkg project pkgs ...
do_importsrcpkg() {
    if [ $# -lt 2 ]; then
        usage_importsrcpkg; exit
    fi
    local prj=$1
    local pkgs=$(get_list "$2")
    shift 2

    obs_check_project "$(pwd)/$prj"
    if [ $? -ne 0 ]; then
        echo "No obs project \"$prj\" in current directory, checkout it firstly"; exit
    fi

    for pkg in $pkgs; do
        yumdownloader --source "$pkg" $@
        if [ $? -ne 0 ]; then
            continue
        else
            local prj_tmp=$(osc search --package "$pkg" | grep "${prj##*/}" | awk '{print $1}')
            if [ "$prj" = "$prj_tmp" ]; then
                echo "$pkg exist"
                continue;
            fi
            osc importsrcpkg ${pkg}*.src.rpm -p "$prj" -n "$pkg"
            cd "$prj/$pkg" || return 1
            osc ar
            osc ci -m "init"
            cd - || return 1
        fi
    done
}


usage_update() {
    echo "update (up): Update a working copy from obs"
    echo ""
    echo "usage:"
    echo "    ${PROG} up"
    echo ""
}

alias_def update up
# do_update
do_update() {
    osc_update
}


usage_setflag() {
    echo "setflag (sflag): Modify or set a defined flag for package"
    echo ""
    echo "usage:"
    echo "    ${PROG} setflag PROJECT REPO ARCH FLAG STATUS PACKAGES|LISTFILE"
    echo ""
    echo "params:"
    echo "      REPO   - setflag for given repository (all/..)"
    echo "      ARCH   - setflag for given arch (all/..)"
    echo "      FLAG   - modify this flag (build/publish/..) for setflag command"
    echo "      STATUS - enable or disable for setflag command"
    echo ""
    echo "example:"
    echo "    ${PROG} setflag PROJECT all all build disable PACKAGE"
    echo "    ${PROG} setflag PROJECT standard_aarch64 all build disable PACKAGE"
    echo ""
}

alias_def setflag sflag
# do_setflag prj repo arch flag status pkgs
#   refer to https://build.opensuse.org/apidocs/index
do_setflag() {
    if [ $# -ne 6 ]; then
        usage_setflag; exit
    fi
    local prj=$1
    local repo=$2
    local arch=$3
    local flag=$4
    local status=$5
    local pkgs=$(get_list "$6")
    local content=""

    for pkg in $pkgs; do
        if [ "$repo" = "all" ]; then
            if [ "$arch" = "all" ]; then
                content="/source/${prj}/${pkg}?cmd=set_flag&flag=${flag}&status=${status}"
            else
                content="/source/${prj}/${pkg}?cmd=set_flag&arch=${arch}&flag=${flag}&status=${status}"
            fi
        else
            if [ "$arch" = "all" ]; then
                content="/source/${prj}/${pkg}?cmd=set_flag&repository=${repo}&flag=${flag}&status=${status}"
            else
                content="/source/${prj}/${pkg}?cmd=set_flag&repository=${repo}&arch=${arch}&flag=${flag}&status=${status}"
            fi
        fi
        echo "set $status for $prj/$pkg ($repo/$arch) $flag flag"
        osc api -X POST "$content" 2> /dev/null
    done
}


usage_buildinfo() {
    echo "buildinfo (binfo): Display the package build infos"
    echo ""
    echo "usage:"
    echo "    ${PROG} binfo PROJECT/REPO/ARCH/PACKAGE"
    echo "    ${PROG} binfo PROJECT REPO ARCH PACKAGE"
    echo ""
}

# obs_get_bdep_tagval info tag
obs_get_bdep_tagval() {
    echo "$1" | sed -r "s/.*$2=\"([^\"]+)\".*$/\1/g" | grep -v "<bdep"
}

alias_def buildinfo binfo
# do_buildinfo prj/repo/arch/pkg
# do_buildinfo prj repo arch pkg
do_buildinfo() {
    # content=/build/<project>/<repository>/<arch>/<package>/_buildinfo
    local content=""
    if [ $# -eq 1 ]; then
        content="/build/$1/_buildinfo"
    elif [ $# -eq 4 ]; then
        content="/build/$1/$2/$3/$4/_buildinfo"
    else
        usage_buildinfo; exit
    fi
    local infos=$(osc api -X GET "$content" 2> /dev/null)
    local bdeps=$(echo "$infos" | grep "<bdep" | grep -v "preinstall")
    local pkgs=()
    OLD_IFS="$IFS"; IFS=$'\n'
    for info in $bdeps; do
        local name=$(obs_get_bdep_tagval "$info" name)
        local epoch=$(obs_get_bdep_tagval "$info" epoch)
        local version=$(obs_get_bdep_tagval "$info" version)
        local release=$(obs_get_bdep_tagval "$info" release)
        local pkg="$name-$version-$release"
        if [ -n "$epoch" ]; then
            pkg="$name-$epoch:$version-$release"
        fi
        array_add pkgs "$pkg"
    done
    IFS="$OLD_IFS"
    array_uniq pkgs
}


usage_getbin() {
    echo "getbin (gbin): Get binary from the obs"
    echo ""
    echo "usage:"
    echo "    ${PROG} getbin URL"
    echo ""
    echo "example:"
    echo "    ${PROG} getbin https://xxx/project/package/repo/arch/binaryname"
    echo "    ${PROG} getbin project/package/repo/arch/binaryname"
    echo ""
    echo "notes:"
    echo "    URL - you can copy the url from the obs directly"
    echo ""
}

alias_def getbin gbin
# do_getbin url
do_getbin() {
    if [ $# -ne 1 ]; then
        usage_getbin; exit
    fi
    # project/package/repo/arch/binaryname
    local prj=""
    eval $(echo "$1" | awk -F "/" '{
        if (NF >= 5) {
            printf("prj=%s;pkg=%s;repo=%s;arch=%s;bname=%s;", $(NF-4), $(NF-3), $(NF-2), $(NF-1), $NF)
        }
    }')
    if [ -z "$prj" ]; then
        echo "url format error"
        exit
    fi
    # /build/project/repo/arch/package/binaryname
    local content="/build/$prj/$repo/$arch/$pkg/$bname"

    echo "downloading $bname ..."
    osc api -X GET "$content" > "$bname" 2> /dev/null
}


usage_getbinaries() {
    echo "getbinaries (gbins): Get binaries from the obs"
    echo ""
    echo "usage:"
    echo "    ${PROG} gbins PROJECT REPO ARCH PACKAGES|LISTFILE"
    echo ""
}

alias_def getbinaries gbins
# do_getbinaries prj repo arch pkgs
do_getbinaries() {
    if [ $# -ne 4 ]; then
        usage_getbinaries; exit
    fi
    local pkgs=$(get_list "$4")

    for pkg in $pkgs; do
        osc getbinaries "$1" "$pkg" "$2" "$3" 2> /dev/null
    done
}


usage_getbinlist() {
    echo "getbinlist (gbinl): Get binary list from obs"
    echo ""
    echo "usage:"
    echo "    ${PROG} gbinl PROJECT PACKAGE"
    echo "    ${PROG} gbinl PROJECT REPO ARCH PACKAGE"
    echo ""
}

alias_def getbinlist gbinl
# do_getbinlist prj pkg
# do_getbinlist prj repo arch pkg
do_getbinlist() {
    if [ $# -eq 2 ]; then
        local prj=$1
        local pkg=$2
        local results=$(osc results $prj $pkg 2> /dev/null)
        local repo_arch=$(echo "$results" | awk '{print $1"/"$2}')
        local k=1
        for i in $(echo "$repo_arch"); do
            # /build/project/repo/arch/package
            local content="/build/$prj/$i/$pkg"
            color_print "$CLR_GREEN" "$k) $i:"
            osc api -X GET "$content" 2> /dev/null
            ((k++))
        done
    elif [ $# -eq 4 ]; then
        local content="/build/$1/$2/$3/$4"
        osc api -X GET "$content" 2> /dev/null | grep "\.rpm" | awk '{print $2}' | sed -e 's/"//g' -e 's/filename=//g'
    else
        usage_getbinlist; exit
    fi
}


usage_getfile() {
    echo "getfile (gfile): Get file from the obs"
    echo ""
    echo "usage:"
    echo "    ${PROG} gfile URL"
    echo ""
    echo "example:"
    echo "    ${PROG} gfile https://xxx/project/package/filename?xxxx"
    echo "    ${PROG} gfile project/package/filename"
    echo ""
    echo "notes:"
    echo "    URL - you can copy the url from the obs directly"
    echo ""
}

alias_def getfile gfile
# do_getfile url
do_getfile() {
    if [ $# -ne 1 ]; then
        usage_getfile; exit
    fi
    # xxx/project/package/filename?xxxx
    local url=$(echo "$1" | sed 's/\?.*//')
    # xxx/project/package/filename
    local prj=""
    eval $(echo "$url" | awk -F "/" '{
        if (NF >= 3) {
            printf("prj=%s;pkg=%s;fname=%s;", $(NF-2), $(NF-1), $NF)
        }
    }')
    if [ -z "$prj" ]; then
        echo "url format error"; exit
    fi
    # project/package/filename
    local content="$prj/$pkg/$fname"
    local newfile=${fname##*:}

    echo "downloading $newfile ..."
    osc co "$content" 2> /dev/null
    if [ $? -eq 0 ] && [ "$fname" != "$newfile" ]; then
        mv "$fname" "$newfile"
    fi
}


usage_list() {
    echo "list (ls): List the packages of project from obs"
    echo ""
    echo "usage:"
    echo "    ${PROG} ls PROJECT REPO ARCH STATE"
    echo ""
    echo "parameter:"
    echo "    STATE - package state, value as follows:"
    echo "        s scheduled"
    echo "        S signing"
    echo "          disabled"
    echo "        B broken"
    echo "        f finished"
    echo "        b blocked"
    echo "        % building"
    echo "        L locked"
    echo "        . succeeded"
    echo "        U unresolvable"
    echo "        d dispatching"
    echo "        F failed"
    echo "        x excluded"
    echo ""
}

alias_def list ls
# do_list prj repo arch state
do_list() {
    if [ $# -ne 4 ]; then
        usage_list; exit
    fi
    local prj=$1
    local repo=$2
    local arch=$3
    local state=$4
    local pkgs=$(osc pr $prj -s $state -r $repo -a $arch -q -V 2> /dev/null | sed 1d | sed '$d' | sed '$d' | awk '{print $2}')

    if [ -n "$pkgs" ]; then
        echo "$pkgs"
    fi
}

usage_unresolved() {
    echo "unresolved (unres): Analyse the unresolved packages from obs"
    echo ""
    echo "usage:"
    echo "    ${PROG} unres PROJECT REPO ARCH all ..."
    echo "    ${PROG} unres PROJECT REPO ARCH PACKAGES|LISTFILE ..."
    echo ""
    echo "params:"
    echo "    ... - dnf global options, like: --repo reponame"
    echo ""
}

alias_def unresolved unres
# do_unresolved prj repo arch pkgs ...
do_unresolved() {
    if [ $# -lt 4 ]; then
        usage_unresolved; exit
    fi
    local prj=$1
    local repo=$2
    local arch=$3
    local unres_pkgs=""
    if [ "$4" = "all" ]; then
        unres_pkgs=$(osc pr $prj -s U -r $repo -a $arch -q -V 2> /dev/null | sed 1d | sed '$d' | sed '$d' | awk '{print $2}')
    else
        unres_pkgs=$(get_list "$4")
    fi
    shift 4
    dnf_set_global_option "$@"
    # do query
    {
    printf "%s|%s|%s|%s\n" "# PKG" "# DEP" "# BIN" "# SRC"
    for x in $unres_pkgs; do
        local relys=$(osc r $prj $x -r $repo -a $arch -v 2> /dev/null | grep -v unresolv | awk '{print $3}' | sed -r 's/^\(//g')
        for y in $relys; do
            local bin=$(dnf_get_whatprovides "$y")
            local src="Not-Found"
            if [ -z "$bin" ]; then
                bin="Not-Found"
            else
                src=$(dnf_get_source "$bin")
                if [ -z "$src" ]; then
                    src=$bin
                fi
            fi
            printf "%s|%s|%s|%s\n" $x $y $bin $src
        done
    done
    } | format_column '|'
}

usage_pkgresults() {
    echo "pkgresults (pkgr): Shows package-wide build results"
    echo ""
    echo "usage:"
    echo "    ${PROG} pkgr PACKAGE"
    echo ""
}

alias_def pkgresults pkgr
# do_pkgresults pkg
do_pkgresults() {
    if [ $# -ne 1 ]; then
        usage_pkgresults; exit
    fi
    local package=$1
    local projects=$(osc search --package "$package" 2> /dev/null | sed '0,/^# Project/d' | awk '{print $1}')

    local i=1
    for prj in $projects; do
        local results=$(osc results "$prj" "$package" 2> /dev/null)
        color_print ${COLOR_YELLOW} "$i) $prj"
        echo "${results}" | awk -F '\n' '{
            for (k = 1; k <= NF; k++) {
                printf("\t%s\n", $k)
            }
        }'
        ((i++))
    done
}


usage_getbuildlogs() {
    echo "getbuildlogs (gblogs): Get build logs from the obs"
    echo ""
    echo "usage:"
    echo "    ${PROG} gblogs PROJECT REPO ARCH PACKAGES|LISTFILE"
    echo ""
}

alias_def getbuildlogs gblogs
# do_getbuildlogs prj repo arch pkgs
do_getbuildlogs() {
    if [ $# -ne 4 ]; then
        usage_getbuildlogs; exit
    fi
    local pkgs=$(get_list "$4")

    for pkg in $pkgs; do
        local content="/build/$1/$2/$3/$pkg/_log"
        osc api -X GET "$content" 2> /dev/null 1> "${pkg}"_log
    done
}


usage_getjobtime() {
    echo "getjobtime (gjobt): Get the job average time from the obs"
    echo ""
    echo "usage:"
    echo "    ${PROG} gjobt PROJECT REPO ARCH PACKAGES|LISTFILE"
    echo ""
}

__do_getjobtime() {
    printf "%s,%s,%s,%s,%s,%s\n" "Package" "Polynomial" "Count" "Total(s)" "Average(s)" "Average(m)"
    for pkg in $pkgs; do
        local content="/build/$1/$2/$3/_jobhistory?package=$pkg&code=succeeded&limit=10"
        local infos=$(osc api -X GET "$content" 2> /dev/null)
        local values=$(echo "$infos" | grep "starttime" | awk '{print $8i","$9}' | sed -r 's/"|starttime|endtime|=//g')
        local cnt=$(echo "$values" | wc -l)
        local total=0
        local poly=""
        for val in $values; do
            local starttime=$(echo "$val" | awk -F ','  '{print $1}')
            local endtime=$(echo "$val" | awk -F ',' '{print $2}')
            local difftime=$((endtime-starttime))
            if [ -z "$poly" ]; then
                poly="$difftime"
            else
                poly="$poly+$difftime"
            fi
            total=$((total+endtime-starttime))
        done
        if [ -z "$poly" ]; then
            poly="none"
        fi
        local divisor=$((cnt))
        local average="$((total/divisor)).$((total%divisor))"
        divisor=$((cnt*60))
        local average_m="$((total/divisor)).$((total%divisor))"
        printf "%s,%s,%s,%s,%s,%s\n" "$pkg" $poly "$cnt" $total $average $average_m
    done
}

alias_def getjobtime gjobt
# do_getjobtime prj repo arch pkgs
do_getjobtime() {
    if [ $# -ne 4 ]; then
        usage_getjobtime; exit
    fi
    local pkgs=$(get_list "$4")

    __do_getjobtime "$1" "$2" "$3" "$pkgs" | format_column ','
}


usage_rdelete() {
printf "rdelete (rdel): Delete a project or packages on the server.

usage:
    ${PROG} rdelete PROJECT PACKAGES
    ${PROG} rdelete PROJECTS

"
}

alias_def rdelete rdel
# do_rdelete project [packages]
do_rdelete() {
    local prj=""
    local pkgs=""

    if [ $# -eq 1 ]; then
        prj=$(get_list "$1")
    elif [ $# -eq 2 ]; then
        prj=$1
        pkgs=$(get_list "$2")
    else
        usage_rdelete; exit
    fi

    if [ -z "$pkgs" ]; then
        for p in $prj; do
            echo "delete project $p"
            osc rdelete "$p" -r -m "delete $p" 2> /dev/null
        done
    elif [ -n "$prj" ]; then
        for pkg in $pkgs; do
            echo "delete package $prj/$pkg"
            osc rdelete "$prj" "$pkg" -m "delete $prj/$pkg" 2> /dev/null
        done
    fi
}

usage_rebuild() {
printf "rebuild: Triggers packages rebuild for an obs project.

usage:
    ${PROG} rebuild PROJECT

"
}

# do_rebuild project
do_rebuild() {
    if [ $# -ne 1 ]; then
        usage_rebuild; exit
    fi
    local prj="$1"

    # POST /build/<project>?cmd=rebuild
    osc api -X POST "/build/$prj?cmd=rebuild" 2> /dev/null
}
